package com.advanced.generic;

import java.sql.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 泛型-demo
 * 泛型概述、泛型特性、泛型使用、泛型方法、泛型上下边界、泛型数组
 * 博客参考：https://www.cnblogs.com/Blue-Keroro/p/8875898.html#top
 * 1.常见的如T、E、K、V等形式的参数常用于表示泛型
 * 2.泛型赋值不能是基础类型
 * 3.泛型擦除：Java中的泛型，只在编译阶段有效。在编译过程中，正确检验泛型结果后，会将泛型的相关信息擦出，并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说，泛型信息不会进入到运行时阶段。
 * 4.静态泛型方法
 * 5.不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的，编译时会出错
 * 6.泛型类型为什么不能设置成基本数据类型
 * 7.泛型类，是在实例化类的时候指明泛型的具体类型；泛型方法，是在调用方法的时候指明泛型的具体类型 。
 * 8.10到泛型面试题：https://cloud.tencent.com/developer/article/1033693
 * @author: zhangxiaohu@souche.com
 * @date: 2022/9/20
 */
public class GenericTest2 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        /**
         * 类型转换异常-demo
         * 1.需求场景：为了解决类似这样的问题（在编译阶段就可以解决），泛型应运而生
         */
        List arrayList = new ArrayList<>();
        arrayList.add(123);
        arrayList.add(34);
        arrayList.add("string");
        for (int i = 0; i < arrayList.size(); i++) {
            //Integer inte = (Integer) arrayList.get(i);
            System.out.println("泛型测试：");
        }
        /**
         * 2.泛型：类型擦除-验证
         */
        List<String> str = new ArrayList<String>();
        List<Integer> inte2 = new ArrayList<Integer>();
        Class<? extends List> aClass = str.getClass();
        Class<? extends List> aClass1 = inte2.getClass();
        System.out.println("类型相同:" + (aClass.equals(aClass1)));

        //泛型类+泛型方法
        Generic<String> stringGeneric = new Generic<>();
        System.out.println("输出泛型方法："+stringGeneric.genericMethod(Class.forName("java.lang.String")).getClass().getName());

        //泛型方法-可变参数
        stringGeneric.printMsg(23,"34",900,"890",true);
    }

}

/**
 * 泛型类-demo
 * 此处T可以是任意标识，常见有T、E、K、V等形式的参数标识泛型
 * 在泛型实例化时，必须指定T的具体类型
 *
 * @param <T>
 */
class Generic<T> {
    //key这个成员变量的类型为T,T的类型由外部指定
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public Generic() {

    }

    //泛型构造方法形参key的类型也为T，T的类型由外部指定
    public T getKey() {
        return key;
    }

    ////泛型方法getKey的返回值类型为T，T的类型由外部指定
    public void setKey(T key) {
        this.key = key;
    }

    /**
     * 不是泛型方法，是普通的成员方法：
     * 我想说的其实是这个，虽然在方法中使用了泛型，但是这并不是一个泛型方法。
     * 这只是类中一个普通的成员方法，只不过他的返回值是在声明泛型类已经声明过的泛型。
     * 所以在这个方法中才可以继续使用 T 这个泛型。
     * @return
     */
    public T getKey2() {
        return key;
    }

    /**
     * 泛型方法-demo
     *  泛型类，是在实例化类的时候指明泛型的具体类型；泛型方法，是在调用方法的时候指明泛型的具体类型 。
     *  1）public 与 返回值中间<T>非常重要，可以理解为声明此方法为泛型方法。
     *  2）只有声明了<T>的方法才是泛型方法，泛型类中的使用了泛型的成员方法并不是泛型方法。
     *  3）<T>表明该方法将使用泛型类型T，此时才可以在方法中使用泛型类型T。
     *  4）与泛型类的定义一样，此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型。
     *  泛型类和类中泛型方法关系：由于泛型在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型，编译器也能够正确的识别泛型类中的泛型方法
     * @param tClass
     * @return
     * @param <T>
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public <E> E genericMethod(Class<E> tClass) throws InstantiationException, IllegalAccessException {
        E  instance = tClass.newInstance();
        return instance;
    }

    /**
     * 在泛型类中声明了一个泛型方法，使用泛型T，注意这个T是一种全新的类型，可以与泛型类中声明的T不是同一种类型
     * @param tClass
     * @return
     * @param <T>
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public <T> T genericMethod2(Class<T> tClass) throws InstantiationException, IllegalAccessException {
        T  instance = tClass.newInstance();
        return instance;
    }

    /**
     * 泛型方法与可变参数
     */
    public <T> void printMsg(T... args){
        for (T t:args) {
            System.out.println("可变参数："+t);
        }
    }

    /**
     * 泛型上下边界
     * 在泛型方法中添加上下边界限制的时候，必须在权限声明与返回值之间的<T>上添加上下边界，即在泛型声明的时候添加
     * 泛型的上下边界添加，必须与泛型的声明在一起
     */
    public <T extends Number> Integer genericMethod3(GenericInside<? extends Number> tClass) {
        Integer instance = (Integer)tClass.getClass().getModifiers();
        return instance;
    }

    class GenericInside<T extends Number>{

    }
}

interface GenericInterface<T> {
    public T next();
}

/**
 * 未传入泛型实参时，与泛型类的定义相同，在声明类的时候，需将泛型的声明也一起加到类中
 * 即class FruitGenerator<T> implements GenericInterface<T>
 * 如果不声明泛型，如：class FruitGenerator implements GenericInterface<T>  编译会报“Unknown class”
 *
 * @param <T>
 */
class FruitGenerator<T> implements GenericInterface<T> {

    @Override
    public T next() {
        return null;
    }
}

/**
 * 传入泛型实参时：
 * 定义一个生产器类实现这个接口，虽然我们只创建了一个泛型接口GenericInterface<T>
 * 单时我们可以为T传入无数个实参，形成无数种类型的GenericInterface<T>接口
 * 在实现类实心泛型接口时，如已将泛型类型传入实参类型，则所有使用泛型的地方都要替换成传入的实参类型
 * 即：GenericInterface<T>，public T next();中的T都要替换成传入的String类型
 */
class FruitGenerator1 implements GenericInterface<String> {
    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

/**
 * 传入的实参类型需与泛型的类型参数类型相同，即为String
 */
class GenericClassTest {
    public static void main(String[] args) {
        Generic<String> stringGeneric = new Generic<String>("89dk");
        System.out.println(stringGeneric.getKey());
    }
}

/**
 * 通配符-demo
 * 同一种泛型可以对应多个版本（因为参数类型是不确定的），不同版本的泛型类实例是不兼容的。
 * 此处的？和Number、String、Integer一样都是一种实际的类型，可以把？看成所有类型的父类。是一种真实的类型。
 */
class WildcardTest {
    public static void main(String[] args) {
        Generic<Integer> gInteger = new Generic<Integer>(123);
        Generic<Number> gNumber = new Generic<Number>(456);
        new WildcardTest().showKeyValue1(gNumber);

// showKeyValue这个方法编译器会为我们报错：Generic<java.lang.Integer>
// cannot be applied to Generic<java.lang.Number>
// showKeyValue(gInteger);
        System.out.println("-------------------");
        //泛型数组：
        Generic<String> str1 = new Generic<>();
        str1.setKey("23");
        Generic<String> str2 = new Generic<>();
        str2.setKey("23AB");
        Generic<String>[] var = new Generic[2];
        var[0] = str1;
        var[1] = str2;
        for (int i = 0; i < var.length; i++) {
            System.out.println("generic数组："+var[i].getKey());
        }


        List<?>[] lsa = new List<?>[10];
        List<Integer> list1 = new ArrayList<Integer>();
        List<String> list2 = new ArrayList<String>();
        List<Boolean> list3 = new ArrayList<Boolean>();
        list1.add(new Integer(3));
        list2.add("90");
        list3.add(true);
        lsa[0] = list1;
        lsa[1] = list2;
        lsa[2] = list3;
        Integer i = (Integer) lsa[0].get(0);
        Object o1 = lsa[1].get(0);
        Object o2 = lsa[2].get(0);
        System.out.println("泛型数组："+i);
        System.out.println("泛型数组："+o1);
        System.out.println("泛型数组："+o2);

    }

    public void showKeyValue1(Generic<Number> obj) {
        System.out.println("泛型测试:" + String.format("key value is %s", obj.getKey()));
    }

}
